<html><head><meta charset="utf-8"><title>02 String、Long 源码解析和面试题 -慕课专栏</title>
			<meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1">
			<meta name="renderer" content="webkit">
			<meta property="qc:admins" content="77103107776157736375">
			<meta property="wb:webmaster" content="c4f857219bfae3cb">
			<meta http-equiv="Access-Control-Allow-Origin" content="*">
			<meta http-equiv="Cache-Control" content="no-transform ">
			<meta http-equiv="Cache-Control" content="no-siteapp">
			<link rel="apple-touch-icon" sizes="76x76" href="https://www.imooc.com/static/img/common/touch-icon-ipad.png">
			<link rel="apple-touch-icon" sizes="120x120" href="https://www.imooc.com/static/img/common/touch-icon-iphone-retina.png">
			<link rel="apple-touch-icon" sizes="152x152" href="https://www.imooc.com/static/img/common/touch-icon-ipad-retina.png">
			<link href="https://moco.imooc.com/captcha/style/captcha.min.css" rel="stylesheet">
			<link rel="stylesheet" href="https://www.imooc.com/static/moco/v1.0/dist/css/moco.min.css?t=201907021539" type="text/css">
			<link rel="stylesheet" href="https://www.imooc.com/static/lib/swiper/swiper-3.4.2.min.css?t=201907021539">
			<link rel="stylesheet" href="https://static.mukewang.com/static/css/??base.css,common/common-less.css?t=2.5,column/zhuanlanChapter-less.css?t=2.5,course/inc/course_tipoff-less.css?t=2.5?v=201907051055" type="text/css">
			<link charset="utf-8" rel="stylesheet" href="https://www.imooc.com/static/lib/ueditor/themes/imooc/css/ueditor.css?v=201907021539"><link rel="stylesheet" href="https://www.imooc.com/static/lib/baiduShare/api/css/share_style0_16.css?v=6aba13f0.css"></head>
			<body><div id="main">


<div class="main-con hide-menu">
    <!-- 左侧菜单 & 索引 -->
    
    <div class="right-content" style="padding-left: 0px;">
        <div class="container clearfix" id="top" style="width: 1134px; display: block;">
            
            
            <div class="center_con js-center_con l" style="width: 1134px;">
                <div class="article-con">
                                            <!-- 买过的阅读 -->
                        

                    
                    <div class="art-title" style="margin-top: 0px;">
                        02 String、Long 源码解析和面试题 
                    </div>
                    <div class="art-info clearfix">
                        
                        <span class="l">
                            更新时间：2019-08-27 14:23:18
                        </span>
                    </div>
                    <div class="art-top">
                                                <img src="https://img1.mukewang.com/5d6353210001e30406400359.jpg" alt="">
                                                                        <div class="famous-word-box">
                            <img src="https://www.imooc.com/static/img/column/bg-l.png" alt="" class="bg1 bg">
                            <img src="https://www.imooc.com/static/img/column/bg-r.png" alt="" class="bg2 bg">
                            <div class="famous-word">劳动是一切知识的源泉。<p class="author">——陶铸</p></div>
                        </div>
                                            </div>
                    <div class="art-content js-lookimg">
                        <div id="article_content">
                            <div class="cl-preview-section"><h2 id="引导语" style="font-size: 30px;">引导语</h2>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">String 和 Long 大家都很熟悉，本小节主要结合实际的工作场景，来一起看下 String 和 Long 的底层源码实现，看看平时我们使用时，有无需要注意的点，总结一下这些 API 都适用于哪些场景。</p>
</div><div class="cl-preview-section"><h2 id="string" style="font-size: 30px;">1 String</h2>
</div><div class="cl-preview-section"><h3 id="不变性">1.1 不变性</h3>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">我们常常听人说，HashMap 的 key 建议使用不可变类，比如说 String 这种不可变类。这里说的不可变指的是类值一旦被初始化，就不能再被改变了，如果被修改，将会是新的类，我们写个 demo 来演示一下。</p>
</div><div class="cl-preview-section"><pre class="  language-java"><code class="prism  language-java">String s <span class="token operator">=</span><span class="token string">"hello"</span><span class="token punctuation">;</span>
s <span class="token operator">=</span><span class="token string">"world"</span><span class="token punctuation">;</span>
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">从代码上来看，s 的值好像被修改了，但从 debug 的日志来看，其实是 s 的内存地址已经被修改了，也就说 s =“world” 这个看似简单的赋值，其实已经把 s 的引用指向了新的 String，debug 的截图显示内存地址已经被修改，两张截图如下：</p>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;"><img class="" src="https://img.mukewang.com/5d5fc04a0001c6a508840096.png" data-original="//img.mukewang.com/5d5fc04a0001c6a508840096.png" alt="图片描述"><img class="" src="https://img.mukewang.com/5d5fc06400019cc210540090.png" data-original="//img.mukewang.com/5d5fc06400019cc210540090.png" alt="图片描述">我们从源码上查看一下原因：</p>
</div><div class="cl-preview-section"><pre class="  language-java"><code class="prism  language-java"><span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">class</span> <span class="token class-name">String</span>
    <span class="token keyword">implements</span> <span class="token class-name">java<span class="token punctuation">.</span>io<span class="token punctuation">.</span>Serializable</span><span class="token punctuation">,</span> Comparable<span class="token operator">&lt;</span>String<span class="token operator">&gt;</span><span class="token punctuation">,</span> CharSequence <span class="token punctuation">{</span>
    <span class="token comment">/** The value is used for character storage. */</span>
    <span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token keyword">char</span> value<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>    
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">我们可以看出来两点：</p>
</div><div class="cl-preview-section"><ol>
<li style="font-size: 20px; line-height: 38px;">String 被 final 修饰，说明 String 类绝不可能被继承了，也就是说任何对 String 的操作方法，都不会被继承覆写；</li>
<li style="font-size: 20px; line-height: 38px;">String 中保存数据的是一个 char 的数组 value。我们发现 value 也是被 final 修饰的，也就是说 value 一旦被赋值，内存地址是绝对无法修改的，而且 value 的权限是  private 的，外部绝对访问不到，String 也没有开放出可以对 value 进行赋值的方法，所以说 value 一旦产生，内存地址就根本无法被修改。</li>
</ol>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">以上两点就是 String 不变性的原因，充分利用了 final 关键字的特性，如果你自定义类时，希望也是不可变的，也可以模仿 String 的这两点操作。</p>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">因为 String 具有不变性，所以 String 的大多数操作方法，都会返回新的 String，如下面这种写法是不对的：</p>
</div><div class="cl-preview-section"><pre class="  language-java"><code class="prism  language-java">String str <span class="token operator">=</span><span class="token string">"hello world !!"</span><span class="token punctuation">;</span>
<span class="token comment">// 这种写法是替换不掉的，必须接受 replace 方法返回的参数才行，这样才行：str = str.replace("l","dd");</span>
str<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token string">"l"</span><span class="token punctuation">,</span><span class="token string">"dd"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
</div><div class="cl-preview-section"><h3 id="字符串乱码">1.2 字符串乱码</h3>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">在生活中，我们经常碰到这样的场景，进行二进制转化操作时，本地测试的都没有问题，到其它环境机器上时，有时会出现字符串乱码的情况，这个主要是因为在二进制转化操作时，并没有强制规定文件编码，而不同的环境默认的文件编码不一致导致的。</p>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">我们也写了一个 demo 来模仿一下字符串乱码：</p>
</div><div class="cl-preview-section"><pre class="  language-java"><code class="prism  language-java">String str  <span class="token operator">=</span><span class="token string">"nihao 你好 喬亂"</span><span class="token punctuation">;</span>
<span class="token comment">// 字符串转化成 byte 数组</span>
<span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span> bytes <span class="token operator">=</span> str<span class="token punctuation">.</span><span class="token function">getBytes</span><span class="token punctuation">(</span><span class="token string">"ISO-8859-1"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// byte 数组转化成字符串</span>
String s2 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">String</span><span class="token punctuation">(</span>bytes<span class="token punctuation">)</span><span class="token punctuation">;</span>
log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span>s2<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 结果打印为：</span>
nihao <span class="token operator">?</span><span class="token operator">?</span> <span class="token operator">?</span><span class="token operator">?</span>
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">打印的结果为 ?? ，这就是常见的乱码表现形式。这时候有同学说，是不是我把代码修改成 <code>String s2 = new String(bytes,"ISO-8859-1");</code> 就可以了？这是不行的。主要是因为 ISO-8859-1 这种编码对中文的支持有限，导致中文会显示乱码。唯一的解决办法，就是在所有需要用到编码的地方，都统一使用 UTF-8，对于 String 来说，getBytes 和 new String 两个方法都会使用到编码，我们把这两处的编码替换成 UTF-8 后，打印出的结果就正常了。</p>
</div><div class="cl-preview-section"><h3 id="首字母大小写">1.3 首字母大小写</h3>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">如果我们的项目被 Spring 托管的话，有时候我们会通过 <code>applicationContext.getBean(className);</code> 这种方式得到 SpringBean，这时 className 必须是要满足首字母小写的，除了该场景，在反射场景下面，我们也经常要使类属性的首字母小写，这时候我们一般都会这么做：</p>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;"><code>name.substring(0, 1).toLowerCase() + name.substring(1);</code>，使用 substring 方法，该方法主要是为了截取字符串连续的一部分，substring 有两个方法：</p>
</div><div class="cl-preview-section"><ol>
<li style="font-size: 20px; line-height: 38px;"><code>public String substring(int beginIndex, int endIndex)</code> beginIndex：开始位置，endIndex：结束位置；</li>
<li style="font-size: 20px; line-height: 38px;"><code>public String substring(int beginIndex)</code>beginIndex：开始位置，结束位置为文本末尾。</li>
</ol>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">substring 方法的底层使用的是字符数组范围截取的方法 ：<code>Arrays.copyOfRange(字符数组, 开始位置, 结束位置);</code>从字符数组中进行一段范围的拷贝。</p>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">相反的，如果要修改成首字母大写，只需要修改成 <code>name.substring(0, 1).toUpperCase() + name.substring(1)</code> 即可。</p>
</div><div class="cl-preview-section"><h3 id="相等判断">1.4 相等判断</h3>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">我们判断相等有两种办法，equals 和 equalsIgnoreCase。后者判断相等时，会忽略大小写，近期看见一些面试题在问：如果让你写判断两个 String 相等的逻辑，应该如何写，我们来一起看下 equals 的源码，整理一下思路：</p>
</div><div class="cl-preview-section"><pre class="  language-java"><code class="prism  language-java"><span class="token keyword">public</span> <span class="token keyword">boolean</span> <span class="token function">equals</span><span class="token punctuation">(</span>Object anObject<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 判断内存地址是否相同</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span> <span class="token operator">==</span> anObject<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token comment">// 待比较的对象是否是 String，如果不是 String，直接返回不相等</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>anObject <span class="token keyword">instanceof</span> <span class="token class-name">String</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        String anotherString <span class="token operator">=</span> <span class="token punctuation">(</span>String<span class="token punctuation">)</span>anObject<span class="token punctuation">;</span>
        <span class="token keyword">int</span> n <span class="token operator">=</span> value<span class="token punctuation">.</span>length<span class="token punctuation">;</span>
        <span class="token comment">// 两个字符串的长度是否相等，不等则直接返回不相等</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>n <span class="token operator">==</span> anotherString<span class="token punctuation">.</span>value<span class="token punctuation">.</span>length<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">char</span> v1<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token operator">=</span> value<span class="token punctuation">;</span>
            <span class="token keyword">char</span> v2<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token operator">=</span> anotherString<span class="token punctuation">.</span>value<span class="token punctuation">;</span>
            <span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
            <span class="token comment">// 依次比较每个字符是否相等，若有一个不等，直接返回不相等</span>
            <span class="token keyword">while</span> <span class="token punctuation">(</span>n<span class="token operator">--</span> <span class="token operator">!=</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
                <span class="token keyword">if</span> <span class="token punctuation">(</span>v1<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">!=</span> v2<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">)</span>
                    <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
                i<span class="token operator">++</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
            <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">从 equals 的源码可以看出，逻辑非常清晰，完全是根据 String 底层的结构来编写出相等的代码。这也提供了一种思路给我们：如果有人问如何判断两者是否相等时，我们可以从两者的底层结构出发，这样可以迅速想到一种贴合实际的思路和方法，就像 String 底层的数据结构是 char 的数组一样，判断相等时，就挨个比较 char 数组中的字符是否相等即可。</p>
</div><div class="cl-preview-section"><h3 id="替换、删除">1.5 替换、删除</h3>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">替换在工作中也经常使用，有 replace 替换所有字符、replaceAll 批量替换字符串、replaceFirst 替换遇到的第一个字符串三种场景。</p>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">其中在使用 replace 时需要注意，replace 有两个方法，一个入参是 char，一个入参是 String，前者表示替换所有字符，如：<code>name.replace('a','b')</code>，后者表示替换所有字符串，如：<code>name.replace("a","b")</code>，两者就是单引号和多引号的区别。</p>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">需要注意的是， replace 并不只是替换一个，是替换所有匹配到的字符或字符串哦。</p>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">写了一个 demo 演示一下三种场景：</p>
</div><div class="cl-preview-section"><pre class="  language-java"><code class="prism  language-java"><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">testReplace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
  String str <span class="token operator">=</span><span class="token string">"hello word !!"</span><span class="token punctuation">;</span>
  log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"替换之前 :{}"</span><span class="token punctuation">,</span>str<span class="token punctuation">)</span><span class="token punctuation">;</span>
  str <span class="token operator">=</span> str<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token string">'l'</span><span class="token punctuation">,</span><span class="token string">'d'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"替换所有字符 :{}"</span><span class="token punctuation">,</span>str<span class="token punctuation">)</span><span class="token punctuation">;</span>
  str <span class="token operator">=</span> str<span class="token punctuation">.</span><span class="token function">replaceAll</span><span class="token punctuation">(</span><span class="token string">"d"</span><span class="token punctuation">,</span><span class="token string">"l"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"替换全部 :{}"</span><span class="token punctuation">,</span>str<span class="token punctuation">)</span><span class="token punctuation">;</span>
  str <span class="token operator">=</span> str<span class="token punctuation">.</span><span class="token function">replaceFirst</span><span class="token punctuation">(</span><span class="token string">"l"</span><span class="token punctuation">,</span><span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"替换第一个 l :{}"</span><span class="token punctuation">,</span>str<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">//输出的结果是：</span>
替换之前 <span class="token operator">:</span>hello word <span class="token operator">!</span><span class="token operator">!</span>
替换所有字符 <span class="token operator">:</span>heddo word <span class="token operator">!</span><span class="token operator">!</span>
替换全部 <span class="token operator">:</span>hello worl <span class="token operator">!</span><span class="token operator">!</span>
替换第一个 <span class="token operator">:</span>helo worl <span class="token operator">!</span><span class="token operator">!</span>
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">当然我们想要删除某些字符，也可以使用 replace 方法，把想删除的字符替换成 “” 即可。</p>
</div><div class="cl-preview-section"><h3 id="拆分和合并">1.6 拆分和合并</h3>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">拆分我们使用 split 方法，该方法有两个入参数。第一个参数是我们拆分的标准字符，第二个参数是一个 int 值，叫 limit，来限制我们需要拆分成几个元素。如果 limit 比实际能拆分的个数小，按照 limit 的个数进行拆分，我们演示一个 demo：</p>
</div><div class="cl-preview-section"><pre class="  language-java"><code class="prism  language-java">String s <span class="token operator">=</span><span class="token string">"boo:and:foo"</span><span class="token punctuation">;</span>
<span class="token comment">// 我们对 s 进行了各种拆分，演示的代码和结果是：</span>
s<span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token string">":"</span><span class="token punctuation">)</span> 结果<span class="token operator">:</span><span class="token punctuation">[</span><span class="token string">"boo"</span><span class="token punctuation">,</span><span class="token string">"and"</span><span class="token punctuation">,</span><span class="token string">"foo"</span><span class="token punctuation">]</span>
s<span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token string">":"</span><span class="token punctuation">,</span><span class="token number">2</span><span class="token punctuation">)</span> 结果<span class="token operator">:</span><span class="token punctuation">[</span><span class="token string">"boo"</span><span class="token punctuation">,</span><span class="token string">"and:foo"</span><span class="token punctuation">]</span>
s<span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token string">":"</span><span class="token punctuation">,</span><span class="token number">5</span><span class="token punctuation">)</span> 结果<span class="token operator">:</span><span class="token punctuation">[</span><span class="token string">"boo"</span><span class="token punctuation">,</span><span class="token string">"and"</span><span class="token punctuation">,</span><span class="token string">"foo"</span><span class="token punctuation">]</span>
s<span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token string">":"</span><span class="token punctuation">,</span><span class="token operator">-</span><span class="token number">2</span><span class="token punctuation">)</span> 结果<span class="token operator">:</span><span class="token punctuation">[</span><span class="token string">"boo"</span><span class="token punctuation">,</span><span class="token string">"and"</span><span class="token punctuation">,</span><span class="token string">"foo"</span><span class="token punctuation">]</span>
s<span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token string">"o"</span><span class="token punctuation">)</span> 结果<span class="token operator">:</span><span class="token punctuation">[</span><span class="token string">"b"</span><span class="token punctuation">,</span><span class="token string">""</span><span class="token punctuation">,</span><span class="token string">":and:f"</span><span class="token punctuation">]</span>
s<span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token string">"o"</span><span class="token punctuation">,</span><span class="token number">2</span><span class="token punctuation">)</span> 结果<span class="token operator">:</span><span class="token punctuation">[</span><span class="token string">"b"</span><span class="token punctuation">,</span><span class="token string">"o:and:foo"</span><span class="token punctuation">]</span>
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">从演示的结果来看，limit 对拆分的结果，是具有限制作用的，还有就是拆分结果里面不会出现被拆分的字段。</p>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">那如果字符串里面有一些空值呢，拆分的结果如下：</p>
</div><div class="cl-preview-section"><pre class="  language-java"><code class="prism  language-java">String a <span class="token operator">=</span><span class="token string">",a,,b,"</span><span class="token punctuation">;</span>
a<span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token string">","</span><span class="token punctuation">)</span> 结果<span class="token operator">:</span><span class="token punctuation">[</span><span class="token string">""</span><span class="token punctuation">,</span><span class="token string">"a"</span><span class="token punctuation">,</span><span class="token string">""</span><span class="token punctuation">,</span><span class="token string">"b"</span><span class="token punctuation">]</span>
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">从拆分结果中，我们可以看到，空值是拆分不掉的，仍然成为结果数组的一员，如果我们想删除空值，只能自己拿到结果后再做操作，但 Guava（Google 开源的技术工具） 提供了一些可靠的工具类，可以帮助我们快速去掉空值，如下：</p>
</div><div class="cl-preview-section"><pre class="  language-java"><code class="prism  language-java">String a <span class="token operator">=</span><span class="token string">",a, ,  b  c ,"</span><span class="token punctuation">;</span>
<span class="token comment">// Splitter 是 Guava 提供的 API </span>
List<span class="token operator">&lt;</span>String<span class="token operator">&gt;</span> list <span class="token operator">=</span> Splitter<span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">','</span><span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token function">trimResults</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token comment">// 去掉空格</span>
    <span class="token punctuation">.</span><span class="token function">omitEmptyStrings</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token comment">// 去掉空值</span>
    <span class="token punctuation">.</span><span class="token function">splitToList</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span><span class="token punctuation">;</span>
log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"Guava 去掉空格的分割方法：{}"</span><span class="token punctuation">,</span>JSON<span class="token punctuation">.</span><span class="token function">toJSONString</span><span class="token punctuation">(</span>list<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 打印出的结果为：</span>
<span class="token punctuation">[</span><span class="token string">"a"</span><span class="token punctuation">,</span><span class="token string">"b  c"</span><span class="token punctuation">]</span>
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">从打印的结果中，可以看到去掉了空格和空值，这正是我们工作中常常期望的结果，所以推荐使用 Guava 的 API 对字符串进行分割。</p>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">合并我们使用 join 方法，此方法是静态的，我们可以直接使用。方法有两个入参，参数一是合并的分隔符，参数二是合并的数据源，数据源支持数组和 List，在使用的时候，我们发现有两个不太方便的地方：</p>
</div><div class="cl-preview-section"><ol>
<li style="font-size: 20px; line-height: 38px;">不支持依次 join 多个字符串，比如我们想依次 join 字符串 s 和 s1，如果你这么写的话 <code>String.join(",",s).join(",",s1)</code> 最后得到的是 s1 的值，第一次 join 的值被第二次 join 覆盖了；</li>
<li style="font-size: 20px; line-height: 38px;">如果 join 的是一个 List，无法自动过滤掉 null 值。</li>
</ol>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">而 Guava 正好提供了 API，解决上述问题，我们来演示一下：</p>
</div><div class="cl-preview-section"><pre class="  language-java"><code class="prism  language-java"><span class="token comment">// 依次 join 多个字符串，Joiner 是 Guava 提供的 API</span>
Joiner joiner <span class="token operator">=</span> Joiner<span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">","</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">skipNulls</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
String result <span class="token operator">=</span> joiner<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token string">"hello"</span><span class="token punctuation">,</span>null<span class="token punctuation">,</span><span class="token string">"china"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"依次 join 多个字符串:{}"</span><span class="token punctuation">,</span>result<span class="token punctuation">)</span><span class="token punctuation">;</span>

List<span class="token operator">&lt;</span>String<span class="token operator">&gt;</span> list <span class="token operator">=</span> Lists<span class="token punctuation">.</span><span class="token function">newArrayList</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">{</span><span class="token string">"hello"</span><span class="token punctuation">,</span><span class="token string">"china"</span><span class="token punctuation">,</span>null<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"自动删除 list 中空值:{}"</span><span class="token punctuation">,</span>joiner<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span>list<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 输出的结果为；</span>
依次 join 多个字符串<span class="token operator">:</span>hello<span class="token punctuation">,</span>china
自动删除 list 中空值<span class="token operator">:</span>hello<span class="token punctuation">,</span>china
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">从结果中，我们可以看到 Guava 不仅仅支持多个字符串的合并，还帮助我们去掉了 List 中的空值，这就是我们在工作中常常需要得到的结果。</p>
</div><div class="cl-preview-section"><h2 id="long" style="font-size: 30px;">2 Long</h2>
</div><div class="cl-preview-section"><h3 id="缓存">2.1 缓存</h3>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">Long 最被我们关注的就是 Long 的缓存问题，Long 自己实现了一种缓存机制，缓存了从 -128 到 127 内的所有 Long 值，如果是这个范围内的 Long 值，就不会初始化，而是从缓存中拿，缓存初始化源码如下：</p>
</div><div class="cl-preview-section"><pre class="  language-java"><code class="prism  language-java"><span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">LongCache</span> <span class="token punctuation">{</span>
    <span class="token keyword">private</span> <span class="token function">LongCache</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token punctuation">}</span>
    <span class="token comment">// 缓存，范围从 -128 到 127，+1 是因为有个 0</span>
    <span class="token keyword">static</span> <span class="token keyword">final</span> Long cache<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Long</span><span class="token punctuation">[</span><span class="token operator">-</span><span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">128</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">127</span> <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">;</span>

    <span class="token comment">// 容器初始化时，进行加载</span>
    <span class="token keyword">static</span> <span class="token punctuation">{</span>
        <span class="token comment">// 缓存 Long 值，注意这里是 i - 128 ，所以再拿的时候就需要 + 128</span>
        <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> cache<span class="token punctuation">.</span>length<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span>
            cache<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Long</span><span class="token punctuation">(</span>i <span class="token operator">-</span> <span class="token number">128</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
</div><div class="cl-preview-section"><h2 id="面试题" style="font-size: 30px;">3 面试题</h2>
</div><div class="cl-preview-section"><h3 id="为什么使用-long-时，大家推荐多使用-valueof-方法，少使用-parselong-方法">3.1 为什么使用 Long 时，大家推荐多使用 valueOf 方法，少使用 parseLong 方法</h3>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">答：因为 Long 本身有缓存机制，缓存了 -128 到 127 范围内的 Long，valueOf  方法会从缓存中去拿值，如果命中缓存，会减少资源的开销，parseLong 方法就没有这个机制。</p>
</div><div class="cl-preview-section"><h3 id="如何解决-string-乱码的问题">3.2 如何解决 String 乱码的问题</h3>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">答：乱码的问题的根源主要是两个：字符集不支持复杂汉字、二进制进行转化时字符集不匹配，所以在 String 乱码时我们可以这么做：</p>
</div><div class="cl-preview-section"><ol>
<li style="font-size: 20px; line-height: 38px;">所有可以指定字符集的地方强制指定字符集，比如 new String 和 getBytes 这两个地方；</li>
<li style="font-size: 20px; line-height: 38px;">我们应该使用 UTF-8 这种能完整支持复杂汉字的字符集。</li>
</ol>
</div><div class="cl-preview-section"><h3 id="为什么大家都说-string-是不可变的">3.3 为什么大家都说 String 是不可变的</h3>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">答：主要是因为 String 和保存数据的 char 数组，都被 final 关键字所修饰，所以是不可变的，具体细节描述可以参考上文。</p>
</div><div class="cl-preview-section"><h3 id="string-一些常用操作问题，如问如何分割、合并、替换、删除、截取等等问题">3.4 String 一些常用操作问题，如问如何分割、合并、替换、删除、截取等等问题</h3>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">答：这些都属于问 String 的基本操作题目，考察我们平时对 String 的使用熟练程度，可以参考上文。</p>
</div><div class="cl-preview-section"><h2 id="总结" style="font-size: 30px;">总结</h2>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">String 和 Long 在我们工作中使用频率很高，在面试的过程中，考官也喜欢问一些关于实际操作的问题，来考察我们的使用熟练度，所以本文中列举的一些 demo，大家可以试试手，完整的代码可以去 GitHub 上面去拉取。</p>
</div>}
                        </div>
                    </div>
                                            <!-- 买过的阅读 -->
                        <div class="art-next-prev clearfix">
                                                                                                <!-- 已买且开放 或者可以试读 -->
                                    <a href="/read/47/article/843">
                                                                    <div class="prev l clearfix">
                                        <div class="icon l">
                                            <i class="imv2-arrow3_l"></i>
                                        </div>
                                        <p>
                                            01开篇词
                                        </p>
                                    </div>
                                </a>
                                                                                                                            <!-- 已买且开放 或者可以试读 -->
                                    <a href="/read/47/article/845">
                                                                    <div class="next r clearfix">
                                        <p>
                                            03 Java 常用关键字理解
                                        </p>
                                        <div class="icon r">
                                            <i class="imv2-arrow3_r"></i>
                                        </div>

                                    </div>
                                </a>
                                                    </div>
                                    </div>
                <div class="comments-con js-comments-con" id="coments_con">
                </div>

                
            </div>
            
            
            

        </div>
    </div>
</div>

<div class="modal modal-jiaQun-new hide" id="modal-jiaQun">
    <div class="inner" style="">
        <div class="modal-close js-close-jiaQun">
            <i class="imv2-close"></i>
        </div>
        <div class="content">
            <img src="https://img4.mukewang.com/5d634d40000119e505400602.jpg">
            <div class="right-info">
                <div class="title">
                    扫码加入慕课Java核心用户群
                </div>
                <div class="desc">
                                            <p class="mb6">验证信息：<span id="joincode">1909271435058473</span><span class="copy js-copy-joincode">复制</span></p>
                                        <p class="mb6">QQ讨论群号：906691736</p>
                                            <p>QQ群URL：<a href="https://jq.qq.com/?_wv=1027&amp;k=55RtSbJ" target="_blank">点击访问</a></p>
                                    </div>
            </div>
            <p class="tip">若遇到搜索不到QQ群或加群失败，请联系客服邮箱:kf@imooc.com</p>
        </div>
    </div>
</div>
 
<!-- 专栏介绍页专栏评价 -->

<!-- 专栏介绍页底部三条评价 -->

<!-- 专栏阅读页弹层目录和介绍页页面目录 -->

<!-- 专栏阅读页发布回复 -->

<!-- 专栏阅读页发布评论 -->

<!-- 专栏阅读页底部评论 -->

<!-- 专栏阅读 单个 评论 -->

<!-- 新增回复和展开三条以外回复 -->

<!-- 立即订阅的弹窗 -->












</div></body></html>